package websocket

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"github.com/gorilla/websocket"
	"net"
)

// 消息类型
// RFC6455 5.2节或11.8节
const (
	ContinuationMessage = 0
	TextMessage = 1
	BinaryMessage = 2
	CloseMessage = 8
	PingMessage = 9
	PongMessage = 10
)

// 数据帧位
// RFC6455 5.2节
const (
	finalBit = 1 << 7
	rsv1Bit = 1 << 6
	rsv2Bit = 1 << 5
	rsv3Bit = 1 << 4

	opCode = 0xf

	maskBit = 1 << 7
	pladloadLen = 0x7f
)

type Conn struct {
	conn net.Conn
	br *bufio.Reader

	writeBuf []byte // 写缓存

	readFinal bool // 中间变量，用于标记数据帧是否读取结束 RFC6455 5.4节
	readLength int64 //数据长度

	maskKey [4]byte // mask key
}

func newConn(conn net.Conn, br *bufio.Reader) *Conn {
	c := &Conn{
		conn:conn,
		br:br,
		writeBuf:make([]byte, 128),
		readFinal:true,
	}
	return c
}

func (c *Conn) ReadMessage() ([]byte, error) {
	err := c.readFrameHeader()
	if err != nil {
		return nil, err
	}
	// 读取长度加1，是为了简单处理，直接将EOF也读取出来
	var p = make([]byte, c.readLength+1)
	n, err := c.br.Read(p)
	if err != nil {
		return nil, err
	}
	maskBytes(c.maskKey, 0, p[:n])
	return p, nil
}

func (c *Conn) WriteMessage(msg []byte) error {
	if len(msg) > 125 {
		return fmt.Errorf("暂不支持")
	}
	c.writeFrameHeader(msg)
	copy(c.writeBuf[2:], msg)
	_, err := c.conn.Write(c.writeBuf)
	return err
}

func (c *Conn) Close() error {
	return c.conn.Close()
}

func (c *Conn) writeFrameHeader(msg []byte) {
	// 第一个字节（前8位）
	// 默认为文本信息
	b0 := byte(TextMessage)

	// FIN 1
	b0 |= finalBit

	b1 := byte(0)
	b1 |= byte(len(msg))
	c.writeBuf[0] = b0
	c.writeBuf[1] = b1
}

// 读取数据帧头
// RFC 6455文档5.2节
func (c *Conn) readFrameHeader() error {
	// 读取前2个字节数据
	// 一个字节等于8位，也就是读取前16位的数据
	// 根据5.2节可知，第一个字节（前8位）包括FIN 1位，RSV1、RSV2、RSV3各1位，Opcode 4位，
	// 第二个字节（9~16位）包括MASK 1位，Payload length 7位
	p, err := c.read(2)
	if err != nil {
		return err
	}

	// 解析数据帧 RFC6455 5.2节
	if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 {
		return fmt.Errorf("RSV必须为0")
	}
	// 表示这是消息的最后一个片段。第一个片段也有可能是最后一个片段。
	//final := p[0]&finalBit != 0
	frameType := int(p[0]&opCode)

	// 判断FIN和opcode为是否匹配
	// RFC6455 5.4节
	// todo
	switch frameType {
	case ContinuationMessage:
	case TextMessage, BinaryMessage:
	case CloseMessage, PingMessage, PongMessage:
	default:
		return fmt.Errorf("未知的opcode")
	}

	mask := p[1]&maskBit != 0
	if !mask {
		return fmt.Errorf("mask位必须标记为1")
	}

	// 获取数据长度
	// https://tools.ietf.org/html/rfc6455#section-5.2
	// The length of the "Payload data", in bytes: if 0-125, that is the
	// payload length.  If 126, the following 2 bytes interpreted as a
	// 16-bit unsigned integer are the payload length.  If 127, the
	// following 8 bytes interpreted as a 64-bit unsigned integer (the
	// most significant bit MUST be 0) are the payload length.  Multibyte
	// length quantities are expressed in network byte order.
	c.readLength = int64(p[1]&pladloadLen)
	switch c.readLength {
	case 126:
		p, err := c.read(2)
		if err != nil {
			return err
		}
		c.readLength = int64(binary.BigEndian.Uint16(p))
	case 127:
		p, err := c.read(8)
		if err != nil {
			return err
		}
		c.readLength = int64(binary.BigEndian.Uint64(p))
	}

	// 获取mask掩码
	// https://tools.ietf.org/html/rfc6455#section-5.2
	// All frames sent from the client to the server are masked by a
	// 32-bit value that is contained within the frame.  This field is
	// present if the mask bit is set to 1 and is absent if the mask bit
	// is set to 0.
	if mask {
		p, err := c.read(4)
		if err != nil {
			return err
		}
		copy(c.maskKey[:], p)
	}

	if frameType == ContinuationMessage || frameType == TextMessage || frameType == websocket.BinaryMessage {
		return nil
	}

	// todo 其他数据帧类型解析、处理
	return nil
}

func (c *Conn)read(n int) ([]byte, error) {
	p, err := c.br.Peek(n)
	c.br.Discard(len(p))
	return p, err
}

// 只支持英文和数字
func maskBytes(key [4]byte, pos int, b []byte) int {
	for i := range b {
		b[i] ^= key[pos%4]
		pos++
	}
	return pos%4
}